/*global _:true, Backbone:true, $: true, Mustache: true */

;(function() {

	var BTS;
	if (typeof this.BTS === 'undefined') {
		BTS = this.BTS = {};
	} else {
		BTS = this.BTS;
	}

	// This is abstracted out becuase.. well you might not want to do it this way
	BTS.renderTemplate = function(template, data) {
		var tmpl = BTS.templates[template];
		if (!tmpl) {
			throw new Error("Can't find the template: " + template);
		}
		return Mustache.render(tmpl, data);
	};
	
	BTS.SortableRow = Backbone.Model.extend({
		
	});

	BTS.SortableList = Backbone.Collection
			.extend({

				sortByMe : 'cid',

				asc : false,
				
				model: BTS.SortableRow,

				sort : function(opt) {
					opt = opt || {};
					var reallySilent = opt.silent;
					opt.silent = true;
					Backbone.Collection.prototype.sort.call(this, opt);
					if (!reallySilent) {
						this.trigger('sort', this, opt);
					}
				},

				sortBy : function(me, asc) {
					var isValid = (typeof me === 'function' || (typeof me === 'string' && this
							.checkField(me)));
					if (!isValid) {
						return false;
					}

					if (arguments.length === 2) {
						this.asc = asc;
					} else {
						if (this.sortByMe === me) {
							this.asc = !this.asc;
						} else {
							this.asc = false;
						}
					}

					this.sortByMe = me;
					this.sort();
				},

				checkField : function(field) {
					if (this.length === 0) {
						return false;
					}

					if (this.at(0).has(field)) {
						return true;
					}

					if (field === 'id' || field === 'cid') {
						return true;
					}

					return false;
				},

				comparator : function(m1, m2) {
					var me = this.sortByMe;
					var a, b;

					if (typeof me === 'function') {
						a = me(m1);
						b = me(m2);
					} else if (me === 'id') {
						a = m1.id;
						b = m2.id;
					} else if (me === 'cid') {
						a = m1.cid;
						b = m2.cid;
					} else {
						a = m1.get(me);
						b = m2.get(me);
					}

					if (this.asc) {
						return a < b ? -1 : a > b ? 1 : 0;
					} else {
						return b < a ? -1 : b > a ? 1 : 0;
					}
				}

			});

	BTS.SortableTable = Backbone.View.extend({

		tagName : 'table',

		className : 'table',

		events : {
			//'click th' : 'triggerSort'
		},
		
		initialEditable: false,
		
		activate: function(flag){
			var value = this.getTextValue();
			this.restriction = flag;
			if(!flag){//activate false				
				if(!this.restriction && !this.readOnly){
					if(!this.initialEditable){
						this.updateColumns(false);
						if(value.length > 0){
							this.setValue(value);
						}else{
							this.removeAll();
							if(this.addLine){
								this.addFirst();
							}							
						}
						this.initialEditable = true;
					}else{
						this.bindReadonly(false);
					}
				}
			}else{//inactivate true
				//if(!this.readOnly){
					this.bindReadonly(true);
				//}
			}			
		},

		initialize : function(opt) {

			opt = opt || {};
			this.opt = _.defaults(opt, {
				template : 'sortable-table',
				templateHead : 'sortable-table-head',
				tableClass : false,
				sortable : true,
				view : BTS.SortableTableRow,
				emptyMessage : 'No Rows to Display'
			});

			if (this.opt.tableClass) {
				this.$el.addClass(this.opt.tableClass);
			}

			this.pageType = opt.pageType;
			this.restriction = opt.restriction;
			
			this.identity = opt.identity;
			this.readOnly = opt.readOnly;
			this.history = opt.history;
			this.addLine = opt.addLine === undefined?true:opt.addLine;
			this.collection = this.collection || new BTS.SortableList();
			this.rows = {};

			// if anything changes...
			//this.collection.on('reset', this.reset, this);
			//this.collection.on('add', function(model){
			//	this.add(model);
			//}, this);
			//this.collection.on('remove', this.remove, this);
			//this.collection.on('sort', this.sort, this);
		},

		render : function(flag) {	
			var width = this.opt.width;
			$table = $(BTS.renderTemplate(this.opt.template, {
				width: width === undefined?863:width
			}));
			this.tbody = $table.find('tbody');
			this.thead = $table.find('thead');
			if (!this.tbody.length) {
				throw new Error('No table body found');
			} // save hours of debugging
			if (!this.thead.length) {
				throw new Error('No table head found');
			}
			this.reset(flag);

			this.setElement($table);

		    return this;
		},

		reset : function(flag) {
			this.removeAll();
			this.updateColumns(flag);
			//this.updateArrows(); // the sort is already done during the reset so...
			//this.addFirst();
			//this.collection.forEach(this.add, this);
			this.addEmpty();
		},

		updateColumns : function(flag) {
			this.thead.empty();
			this.thead.html(BTS.renderTemplate(this.opt.templateHead, {
				col : this.genColumns(flag)
			}));

		},

		update : function(flag) {
			this.detatchAll();
			this.render(flag);
			this.attachAll();
		},

		genColumns : function(flag) {
			if (!this.opt.col) {
				throw new Error("Columns not defined!");
			}
			
			var columns = [];
			if(!this.readOnly && !this.restriction && !flag){
				this.columns = $.merge(columns, this.opt.col);
				if(this.opt.operations){
					this.columns.push({name: "Operation", field: "operation", type: "Operation", actions: this.opt.operations, editType: "Operation",width: 100});			
					this.operations = this.getOperations(this.opt.operations);
				}
				if(this.opt.history || (this.addLine && !this.readOnly && !this.restriction)){
					this.columns.push({name: "", field: "history", type: "History", editType: "History", width: 60});
				}
			}else{
				this.columns = $.merge(columns, this.opt.col);
			}
			
			return this.columns;
		},
		
		getOperations: function(operations){
			var action = "<input ref=\"operation\" type=\"hidden\" value=\"B\" style=\"display:none;\"><div class=\"btn-group\">";								
						
			_.each(operations, function(i){
				switch(i){
					case 1:
						action += "<button type=\"button\" class=\"btn btn-mini\" main-class=\"btn-info\">B</button>";
						break;
					case 2:
						action += "<button type=\"button\" class=\"btn btn-mini\" main-class=\"btn-success\">C</button>";
						break;
					case 3:
						action += "<button type=\"button\" class=\"btn btn-mini\" main-class=\"btn-warning\">R</button>";
						break;
					case 4:
						action += "<button type=\"button\" class=\"btn btn-mini\" main-class=\"btn-danger\">P</button>";
						break;							
				}	
			});
			
			action += "</div>";
			
			return action;
		},

		triggerSort : function(e) {
			var field = e.target.getAttribute('data-field');
			return this.sortBy(field);
		},

		sortBy : function(col) {
			if (!this.opt.sortable) {
				return;
			}
			if (typeof this.collection.sortBy !== 'function') {
				throw new Error("Collection isn't a SortableList");
			}
			//TODO some magic to check the col array for a sorting function
			this.collection.sortBy(col);
		},

		sort : function() {
			this.detatchAll();
			this.updateArrows();
			this.attachAll();
			this.trigger('sort', this.sortField, this.collection.asc);
		},

		updateArrows : function() {
			if (typeof this.collection.sortByMe === 'string') {
				this.sortField = this.collection.sortByMe;
			} else {
				this.sortField = '';
			}

			var field = this.sortField;
			this.$('.js-sort-sprite').removeClass(
					'icon-arrow-down icon-arrow-up');
			var sprite = this.$('th[data-field="' + field
					+ '"] > .js-sort-sprite');
			if (this.collection.asc) {
				sprite.addClass('icon-arrow-up');
			} else {
				sprite.addClass('icon-arrow-down');
			}
		},

		addEmpty : function() {
			if (this.collection.length === 0 && this.opt.emptyMessage) {
				var colspan = this.columns.length;
				this.tbody.append('<tr class="js-emptyrow"><td colspan='
						+ colspan + ' style="text-align:center">'
						+ this.opt.emptyMessage + '</td></tr>');
			}
		},

		removeEmpty : function() {
			this.tbody.find('.js-emptyrow').remove();
		},

		attachAll : function() {
			this.collection.forEach(function(model) {
				var cid = model.cid;
				var row = this.rows[cid];
				this.tbody.append(row.el);
				row.attach();
			}, this);
		},

		detatchAll : function() {
			_.forEach(this.rows, function(m) {
				m.detach();
			});
		},

		removeAll : function() {
			_.forEach(this.rows, function(m) {
				m.off();
			});
			this.rows = {};
			this.tbody.empty();
		},

		remove : function(model) {
			var cid = model.cid;

			this.rows[cid].remove();
			this.rows[cid].off();

			delete this.rows[cid];
			this.addEmpty();
			this.trigger('remove', model);
		},
						
		addFirst : function(){
			//if(!this.readOnly && !this.restriction){
				var model = new BTS.SortableRow, cid = model.cid;
				var firstRow = this.rows[cid] = new BTS.SortableTableRow({
					model: model, 
					col : this.columns, 
					operations: this.operations, 
					restriction: false, 
					readOnly: this.readOnly, 
					identity: this.identity,
					history: this.history,
					parent: this,
					first: true
				});
				this.tbody.append(firstRow.el);
				firstRow.attach();
				
				this.bindSelect2($(firstRow.el));
			//}
		},

		add : function(model) {
		
			var cid = model.cid;
			var data = {				
				model : model,
				col : this.columns, 
				operations: this.operations,
				restriction: this.restriction,
				readOnly: this.readOnly, 
				identity: this.identity,
				history: this.history,
				parent: this
			};

			var row = this.rows[cid] = new this.opt.view(data);
			this.tbody.append(row.el);
			row.attach();
			 
			this.bindSelect2($(row.el));

			//if (this.collection.length === 1) {
				//this.removeEmpty();
			//}
			this.trigger('add', model);
		},
		
		add_a : function(model) {
			
			var cid = model.cid;
			var data = {				
				model : model,
				col : this.columns, 
				operations: this.operations,
				restriction: false,
				readOnly: this.readOnly, 
				identity: this.identity,
				history: this.history,
				parent: this
			};

			var row = this.rows[cid] = new this.opt.view(data);
			this.tbody.append(row.el);
			row.attach();
			 
			this.bindSelect2($(row.el));

			//if (this.collection.length === 1) {
				//this.removeEmpty();
			//}
			this.trigger('add', model);
		},
		
		setValue: function(value){
			this.removeAll();
			if(value && value.length > 0){
				//this.removeAll();
				var collection = new BTS.SortableList(value);
				this.collection = collection;
				if(!this.initialEditable){
					if(this.restriction || this.readOnly){
						this.collection.forEach(this.add, this);
					}else{
						this.collection.forEach(this.add_a, this);
						if(this.addLine){
							this.addFirst();
						}	
						if(this.restriction){
							this.bindReadonly(true);
						}
					}
				}else{
					this.collection.forEach(this.add_a, this);
					if(this.addLine){
						this.addFirst();
					}	
					if(this.restriction){
						this.bindReadonly(true);
					}	
				}			
			}else{
				if(this.readOnly){
					this.addEmpty();					
				}else{
					if(this.addLine){
						if(!this.initialEditable){
							if(this.restriction){
								this.addEmpty();
							}else{
								this.addFirst();
							}							
						}else{
							this.addFirst();
							if(this.restriction){
								this.bindReadonly(true);
							}
						}
					}else{
						this.addEmpty();
					}					
				}
			}
			if(!this.readOnly && !this.restriction){
				this.initialEditable = true;
			}
		},
		
		bindReadonly: function(flag){
			_.each(this.rows, function(row){
				if(flag){
					row.setReadonly();
				}else{
					row.unsetReadonly();
				}
			});
		},
		
		bindSelect2: function($items){
			var $sel2 = $items.find("input[select2=true]"), pageType = this.pageType;
			$sel2.each(function(index){
				var t = $(this), key = t.attr("id"), classifier = t.attr("classifier"), location = t.attr("location"), ref = t.attr("ref"), hasMeaning = t.attr("hasMeaning"), url = "", min = 0, name = "";
				if(classifier !== undefined && (classifier == "related" || classifier == "alone")){
					url = util.sfbase+"/rest/classifier/getClassifiers";
					min = 1;
				}else if(location && location == "true"){
					url = util.sfbase+"/rest/classifier/getLocations";
					min = 1;
				}else{
					url = util.sfbase+"/rest/groupProperty/get";
					min = 0;
					name = key.split("-")[0];
				}
			    t.select2({
				    placeholder: "Search...",
				    minimumInputLength: min,
				    ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
				    	url: url,
				        dataType: 'json',
				        data: function (term, page) {
				        	var d = {
					            value: term, // search term
					            strname: pageType				                
					        };
				        	if(classifier && (classifier == "related" || classifier == "alone")){
								d["key"] = name;
							}else{
								d["groupname"] = name;
							}
				            return d;
				        },
				        results: function (data, page) { // parse the results into the format expected by Select2.
				            // since we are using custom formatting functions we do not need to alter remote JSON data
				            return {results: data.val};
				        }
				    },
				    formatResult: function(movie) {
				        var markup = "<table class='movie-result'><tr>";
				        markup += "<td><div class='movie-value'>" + movie.value + "</div></td>";
				        if(hasMeaning == "true" || (classifier && (classifier == "related" || classifier == "alone")) || (location && location == "true")){						
							markup += "<td style=\"border-left:1px dotted black;\"><div class='movie-meaning'>" + movie.meaning + "</div></td>";
						}				        
				        markup += "</tr></table>";
				        return markup;
				    }, // omitted for brevity, see the source of this page
				    formatSelection: function(movie) {
				    	if(hasMeaning == "true" || (classifier && (classifier == "related" || classifier == "alone")) || (location && location == "true")){
				    		if(movie.meaning !== undefined && ref != undefined){
				    			$items.find("td[relative='" + ref + "']").html(movie.meaning);
				    		}
				    	}
				        return movie.value;
				    },  // omitted for brevity, see the source of this page
				    dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller			
				    initSelection : function (element, callback) {
						var val = element.attr("init-value"), id = element.attr("init-id");
						if(val != undefined && id != undefined ){							
							callback({id: id, value: val});
						}
					}
				}).on("open", function(e) { 
			    	if(classifier && classifier == "related"){
			    		var $type = $items.find("input[ref=TYPE]");
			    		name = $type.val();//the value of this row's type
			    		if(name == ""){
			    			alert("select type first");
			    			e.preventDefault();
			    		}
			    	}
				});
			});
		},
				
		saveRow: function(value) {
			//add
			var length = this.collection.length;
			if(length == 0 && (this.readOnly || this.restriction)){
				this.removeAll();
			}
			var model = new BTS.SortableRow(value);
			this.collection.add(model);
			this.add(model);					
		},
				
		getValue: function(){
			var arr = [];
			_.each(this.rows, function(item){
				arr.push(JSON.stringify(item.getValue()));
			});
			return arr;
		},
		
		getTextValue: function(){
			var arr = [];
			_.each(this.rows, function(item){
				arr.push(item.getValue());
			});
			return arr;
		},

		at : function(index) {
			var model = this.collection.at(index);
			if (typeof model === 'undefined') {
				return undefined;
			}
			return this.rows[model.cid];
		}

	});

	BTS.SortableTableRow = Backbone.View.extend({

		tagName : 'tr',

		template : 'sortable-table-row',

		context : {},
		
		events : {
			'click input.add-button' : 'add'
		},

		initialize : function(opt) {
			opt = opt || {};
			this.col = opt.col || [];
			this.restriction = opt.restriction;
			this.readOnly = opt.readOnly;
			this.first = opt.first;
			this.identity = opt.identity;
			this.history = opt.history;
			this.operations = opt.operations;
			this.parent = opt.parent;
			
			//this.model.on('change', this.update, this);
			this.render();			
		},

		render : function() {
			
			var data = _.map(this.col, function(field) {
				var row = {};
				row.className = field.className;
				row.relative = field.relative;
				row.cid = this.model.cid;
				row.colName = field.field;
				row.colType = field.editType === undefined?"":field.editType;
				
				if (field.render) {
					var value = field.field ? this.model.get(field.field)
							: undefined;
					row.html = field.render(value, this.model);
				} else if (field.field) {
					var f = field.field;
					if(this.readOnly || this.restriction){
						if(f == "seq"){
							row.text = this.model.cid;
						}else{
							row.text = this.model.get(f);
						}
					}else{
						if(f == "seq"){
							row.text = this.model.cid;
						}else if(f == "operation"){
							if(!this.first){
								row.html = this.operations;	
							}else{
								row.html = "<span></span>";
							}
						}else if(f == "history"){
							if(this.first){
								row.html = "<input type=\"button\" value=\"Add\" class=\"btn btn-primary btn-mini add-button\"/>";
							}else{
								if(this.history){
									row.html = "<input type=\"button\" value=\"History\" class=\"btn btn-primary btn-mini\"/>";	
								}
							}							
						}else{
							var pre = this.identity + "-" + f;
							if(field.editable){
								var type = field.editType, value = this.model.get(f) === undefined?"":this.model.get(f), w = 160;
								if(field.width){
									w = parseInt(field.width) - 10;
								}
								switch(type){
									case "Text":
										row.html = "<input ref=\"" + f + "\" type=\"text\" value=\"" + value + "\" style=\"width:" + w + "px;\">";
										break;
									case "Textarea":
										row.html = "<textarea ref=\"" + f + "\">" + value + "</textarea>";
										break;
									case "Checkbox":
										row.html = "<input ref=\"" + f + "\" type=\"checkbox\" ";
										if(value != undefined && value != ""){
											row.html += "checked=\"" + value + "\" />";
										}else{
											row.html += "/>";
										}										
										break;
									case "SelectDropdown":
										//row.html = "<input id=\"" + row.cid + "_" + f + "_id\" type=\"hidden\" style=\"display:none;\" value=\"" + value + "\"/><div id=\"" + row.cid + "_" + f + "_input\" class=\"like-input normal-input\"></div>";
										var id = pre + "-"+ row.cid, hasMeaning = field.hasMeaning === undefined?false:true;
										row.html = "<input select2=\"true\" hasMeaning=\"" + hasMeaning + "\" ref=\"" + f + "\" type=\"text\" id=\"" + id + "\" style=\"width:" + w + "px;\" ";
										if(value != ""){
											var dispName = this.model.get("displayName") === undefined?value:this.model.get("displayName");											
											row.html += "value=\"" + value + "\" init-value=\"" + dispName + "\" init-id=\"" + value + "\"/>";
										}else{
											row.html += "/>";
										}
										break;
									case "SelectDropdown_c":
										var id = pre + "-"+ row.cid, cls = field.clsType === undefined?"related":"alone";
										row.html = "<input select2=\"true\" classifier=\"" + cls + "\" ref=\"" + f + "\" type=\"text\" id=\"" + id + "\" style=\"width:" + w + "px;\" ";
										if(value != ""){
											var new_id = this.model.get("id") === undefined?"0":this.model.get("id");
											row.html += "value=\"" + value + "\" init-value=\"" + value + "\" init-id=\"" + new_id + "\"/>";///////////
										}else{
											row.html += "/>";
										}
										break;
									case "SelectDropdown_l":
										//row.html = "<input id=\"" + row.cid + "_" + f + "_id\" type=\"hidden\" style=\"display:none;\" value=\"" + value + "\"/><div id=\"" + row.cid + "_" + f + "_input\" class=\"like-input normal-input\"></div>";
										var id = pre + "-"+ row.cid, hasMeaning = field.hasMeaning === undefined?false:true;
										row.html = "<input select2=\"true\" location=\"true\" hasMeaning=\"" + hasMeaning + "\" ref=\"" + f + "\" type=\"text\" id=\"" + id + "\" style=\"width:" + w + "px;\" ";
										if(value != ""){
											var new_id = this.model.get("id") === undefined?"0":this.model.get("id");
											row.html += "value=\"" + value + "\" init-value=\"" + value + "\" init-id=\"" + new_id + "\"/>";//////////
										}else{
											row.html += "/>";
										}
										break;
									case "Custom":
										row.html = field.html;
										break;
									default:
										row.text = value;
								}
							}else{
								row.text = this.model.get(f);
							}
						}
					}
					if(field.hide){
						row.className = "hide-item";
					}
				}
				return row;
			}, this);

			var context = _.extend({}, this.context, {
				col : data,
				model : this.model.toJSON(),
				colspan : this.col.length
			});

			if(this.first){
				this.$el.attr("firstRow", "true");
			}
			this.$el.html(BTS.renderTemplate(this.template, context));	
	
			return this.el;
		},
		
		setReadonly: function(){
			this.$el.find("td").each(function(index){
				var td = $(this), colType = td.attr("colType");
				if(colType === "SelectDropdown" || colType === "SelectDropdown_c" || colType === "SelectDropdown_l"){
					td.find(".select2-container").hide()
						.next().show().attr("disabled", true);
				}else if(colType === "Operation"){
					td.find(".btn-group").hide();
				}else{
					td.find("input").attr("disabled", true);
				}
			});		
		},
		
		unsetReadonly: function(){
			this.$el.find("td").each(function(index){
				var td = $(this), colType = td.attr("colType");
				if(colType === "SelectDropdown" || colType === "SelectDropdown_c" || colType === "SelectDropdown_l"){
					td.find(".select2-container").show()
						.next().hide().removeAttr("disabled");
				}else if(colType === "Operation"){
					td.find(".btn-group").show();
				}else{
					td.find("input").removeAttr("disabled");
				}
			});
		},

		update : function() {
			return this.render();
		},

		detach : function() {
			this.$el.detach();
		},

		attach : function() {
			//no-op
			var $b = this.$el.find(".btn-group"), $bf = $b.find("button:first");
			$bf.addClass($bf.attr("main-class"));
			
			$b.delegate("button", "click", function(){
				var $t = $(this);
				$t.siblings().removeClass().addClass("btn btn-mini").end()
					.addClass($t.attr("main-class")).parent().prev().val($t.html());
			});
		},
		
		validate: function(td){
			
		},
		
		rowValue: function(){
			var obj = {};
			if(this.readOnly || this.restriction){
				this.$el.find("td").each(function(index){
					var td = $(this), colName = td.attr("colName");
					obj[colName] = td.text();
				});				
			}else{
				this.$el.find("td").each(function(index){
					var td = $(this), colName = td.attr("colName"), colType = td.attr("colType"), input = td.find("input[ref='" + colName + "']"), span = td.find(".select2-choice > span").text();
					//if(input.length > 0){
						//validation
						//this.validate(td);
					
						obj[colName] = "";
						if(colType === "Content"){
							obj[colName] = td.html();
						}else if(colType === "Checkbox"){
							obj[colName] = input.attr("checked");
						}else if(colType === "Textarea"){
							obj[colName] = td.find("textarea").val();
						}else if(colType === "SelectDropdown"){
							if(span != ""){
								obj["displayName"] = span === "Search..."?"":span;;
							}
							obj[colName] = input.val() == "0"?"":input.val();
						}else if(colType === "SelectDropdown_c" || colType === "SelectDropdown_l"){
							if(span != ""){
								obj[colName] = span === "Search..."?"":span;;
							}
							obj["id"] = input.val() == "0"?"":input.val();
						}else if(colType === "Text"){
							obj[colName] = input.val();						
						}else{
							obj[colName] = td.html();
						}
					//}
				});
			}
			
			return obj;
		},
				
		getValue: function(){
			var value = this.rowValue();					
			return value;
		},
		
		add: function(){

			var value = this.rowValue();
			this.parent.saveRow(value);
			
			//empty
			this.$el.find("td").each(function(index){
				var td = $(this), colType = td.attr("colType");
				switch(colType){
					case "Content":
						td.html("");
						break;
					case "Text":
						td.find("input").val("");
						break;
					case "Textarea":
						td.find("textarea").val("");
						break;
					case "Checkbox":
						td.find("input:checkbox").removeAttr("checked");
						break;
					case "SelectDropdown":
						td.find("input[select2=true]").select2("data", {id:"", value:""});
						break;
					case "SelectDropdown_c":
						td.find("input[select2=true]").select2("data", {id:"", value:""});
						break;
					case "SelectDropdown_l":
						td.find("input[select2=true]").select2("data", {id:"", value:""});
						break;
					default:
						break;
				}
			});			
		}
	
	});

}.call(this));